<template>
    <div id="home" class="home">
        <div style="position: fixed; left: 0; top: 0; z-index: 10; pointer-events: none; transition: all 1s;">
            <h1 style="width: 100%; height: 100%; padding: 100px 50px; font-size: 24px; color: #fff;">{{ showText }}</h1>
        </div>
    </div>
</template>
<script>
import * as THREE from 'three'
import gsap from 'gsap'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
import {
    OrbitControls
} from 'three/examples/jsm/controls/OrbitControls.js'
import {
    RGBELoader
} from 'three/examples/jsm/loaders/RGBELoader'
import { Water } from 'three/examples/jsm/objects/Water2'

import SKY from '../assets/hdr/sky.hdr'
import LOVE from '../assets/model/scene.glb'
import normalMap0 from '../assets/img/Water_1_M_Normal.jpg'
import normalMap1 from '../assets/img/Water_2_M_Normal.jpg'

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.set(-3.23, 1.98, 4.06);
scene.add(camera);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// 色调编码
renderer.outputEncoding = THREE.sRGBEncoding
// 设置渲染器的色调映射
renderer.toneMapping = THREE.ACESFilmicToneMapping
renderer.toneMappingExposure = 0.5
renderer.physicallyCorrectLights = true

const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(-8, 2, 0)
controls.enableDamping = true

const rgbeloader = new RGBELoader()

export default {
    data() {
        return {
            controls: null,
            sceneObj: [],
            showText: '江畔何人初见月 江月何年初照人',
            startArr: [],
            endArr: [],
            instance: null
        }
    },
    mounted() {
        this.windownEvent()
        this.addModel()
        this.addLight()
        this.addPointLight()
        this.addStar()
        this.addHDR()
        this.sceneChange()
        document.getElementById('home').appendChild(renderer.domElement);
        this.render()
    },
    methods: {
        windownEvent() {
            window.addEventListener('resize', (e) => {
                // 宽高比
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                renderer.setSize(window.innerWidth, window.innerHeight);
                renderer.setPixelRatio(window.devicePixelRatio);
            })
        },
        render() {
            renderer.render(scene, camera);
            requestAnimationFrame(this.render);
        },
        addModel() {
            const gltfLoader = new GLTFLoader()
            const dracoLoader = new DRACOLoader()
            dracoLoader.setDecoderPath('static/draco/')
            gltfLoader.setDRACOLoader(dracoLoader)

            gltfLoader.load(LOVE, gltf => {
                const model = gltf.scene
                // 去除水面
                model.traverse(child => {
                    if (child.name === 'Plane') {
                        child.visible = false
                    }
                    if (child.isMesh) {
                        child.castShadow = true
                        child.receiveShadow = true
                    }
                })
                scene.add(model)
            })
            this.addWaterPlane()
        },
        addWaterPlane() {
            const waterGeometry = new THREE.CircleBufferGeometry(300, 32)
            const water = new Water(waterGeometry, {
                textureWidth: 1024,
                textureHeight: 1024,
                // color: 0x0080ff,
                color: 0xffffff,
                flowDirection: new THREE.Vector2(1, 1),
                scale: 100,
                // 源码中是：const normalMap0 = options.normalMap0 || textureLoader.load( 'textures/water/Water_1_M_Normal.jpg' );但是three源码并没有这个两个纹理图片。所以可以通过手动给这两个属性赋值。。另一种方式是，直接将这两个图片放在：dist/textures/water目录下，这样源码也能找到这两个图片了。
                normalMap0: new THREE.TextureLoader().load(normalMap0),
                normalMap1: new THREE.TextureLoader().load(normalMap1)
            })
            water.rotation.x = -Math.PI / 2
            water.position.y = -0.5;
            scene.add(water)
        },
        addLight() {
            const light = new THREE.DirectionalLight(0xffffff, 1)
            light.position.set(0, 50, 0)
            scene.add(light)
        },
        addHDR() {
            rgbeloader.loadAsync(SKY).then((texture) => {
                texture.mapping = THREE.EquirectangularReflectionMapping
                scene.background = texture
                scene.environment = texture
            })
        },
        addPointLight() {
            const group = new THREE.Group()
            group.position.set(-8, 2.5, -1.5)
            const radius = 3
            const pointArr = []
            for (let i = 0; i < 3; i++) {
                const smallBox = new THREE.Mesh(
                    new THREE.SphereGeometry(0.2, 32, 32),
                    new THREE.MeshBasicMaterial({
                        color: 0xffffff,
                        emissive: 0xffffff,
                        emissiveIntensity: 10
                    })
                )
                pointArr.push(smallBox)
                smallBox.position.set(
                    radius * Math.cos((i * 2 * Math.PI) / 3),
                    Math.cos((i * 2 * Math.PI) / 3),
                    radius * Math.sin((i * 2 * Math.PI) / 3)
                )

                const pointLight = new THREE.PointLight(0xffffff, 40);
                pointLight.position.set(2, 2, 2)
                pointLight.castShadow = true
                // pointLight.shadow.radius = 20
                // pointLight.shadow.mapSize.set(4096, 4096)
                // pointLight.distance = 10
                // pointLight.decay = 1
                smallBox.add(pointLight)
                group.add(smallBox)
            }
            scene.add(group)
            this.motion(group, pointArr, radius)
        },
        motion(group, pointArr, radius) {
            let options = {
                angle: 1
            }
            gsap.to(options, {
                angle: Math.PI * 2,
                duration: 10,
                repeat: -1,
                ease: 'linear',
                onUpdate: () => {
                    group.rotation.y = options.angle
                    pointArr.forEach((node, index) => {
                        node.position.set(
                            radius * Math.cos((index * 2 * Math.PI) / 3),
                            Math.cos((index * 2 * Math.PI) / 3 + options.angle * 5),
                            radius * Math.sin((index * 2 * Math.PI) / 3)
                        )
                    })
                }
            })
        },
        sceneChange() {
            let index = 0
            this.sceneObj = [{
                text: "江畔何人初见月 江月何年初照人",
                callback: () => {
                    console.log("江畔何人初见月")
                    this.sceneChangeCall(
                        new THREE.Vector3(-3.23, 1.98, 4.06),
                        new THREE.Vector3(-8, 2, 0)
                    )
                }
            }, {
                text: "不知江月待何人 但见长江送流水",
                callback: () => {
                    console.log("不知江月待何人")
                    this.sceneChangeCall(
                        new THREE.Vector3(7, 0, 23),
                        new THREE.Vector3(0, 0, 0)
                    )
                }
            }, {
                text: "谁家今夜扁舟子 何处相思明月楼",
                callback: () => {
                    console.log("谁家今夜扁舟子")
                    this.sceneChangeCall(
                        new THREE.Vector3(10, 3, 0),
                        new THREE.Vector3(5, 2, 0)
                    )
                    this.loveAnimate(this.startArr, this.endArr)
                }
            }, {
                text: "玉户帘中卷不去 捣衣砧上拂还来",
                callback: () => {
                    console.log("玉户帘中卷不去")
                    this.sceneChangeCall(
                        new THREE.Vector3(7, 0, 23),
                        new THREE.Vector3(5, 2, 0)
                    )
                }
            }, {
                text: "江水流春去欲尽 江潭落月复西斜",
                callback: () => {
                    console.log("江水流春去欲尽")
                    this.sceneChangeCall(
                        new THREE.Vector3(-20, 1.3, 6.6),
                        new THREE.Vector3(5, 2, 0)
                    )
                }
            }]
            let isAnimate = false
            window.addEventListener('wheel', (e) => {
                // if (!isAnimate) return
                if (e.deltaY > 0) {
                    index++
                    if (index > this.sceneObj.length - 1) {
                        index = 0
                        this.loveRestartAnimate(this.startArr, this.endArr)
                    }
                }
                this.showText =
                    this.sceneObj[index].text
                this.sceneObj[index].callback()
                // 添加防抖
                setTimeout(() => {
                    isAnimate = true
                }, 1000);
            })
        },
        sceneChangeCall(position, target) {
            let timeLine1 = gsap.timeline()
            let timeLine2 = gsap.timeline()

            timeLine1.to(camera.position, {
                x: position.x,
                y: position.y,
                z: position.z,
                duration: 1,
                ease: "Power2.inOut"
            })

            timeLine2.to(camera.target, {
                x: target.x,
                y: target.y,
                z: target.z,
                duration: 1,
                ease: "Power2.inOut"
            })
        },
        addStar() {
            // 生成100个球体
            this.instance = new THREE.InstancedMesh(
                new THREE.SphereGeometry(0.1, 32, 32),
                new THREE.MeshBasicMaterial({
                    color: 0xffffff,
                    emissive: 0xffffff,
                    emissiveIntensity: 10
                }),
                100
            )
            // 随机分布
            for (let i = 0; i < 100; i++) {
                const x = Math.random() * 100 - 50
                const y = Math.random() * 100 - 50
                const z = Math.random() * 100 - 50
                this.startArr.push(new THREE.Vector3(x, y, z))

                let matrix = new THREE.Matrix4()
                matrix.setPosition(x, y, z)
                this.instance.setMatrixAt(i, matrix)
            }
            scene.add(this.instance)

            const line = new THREE.Shape()
            // 绘制爱心曲线
            line.moveTo(25, 25)
            line.bezierCurveTo(25, 25, 20, 0, 0, 0)
            line.bezierCurveTo(-30, 0, -30, 35, -30, 35)
            line.bezierCurveTo(-30, 55, -10, 77, 20, 95)
            line.bezierCurveTo(60, 77, 80, 55, 80, 35)
            line.bezierCurveTo(80, 35, 80, 0, 50, 0)
            line.bezierCurveTo(35, 0, 25, 25, 25, 25)
            const center = new THREE.Vector3(0, 4, 0)
            for (let i = 0; i < 100; i++) {
                let point = line.getPoint(i / 100)
                this.endArr.push(new THREE.Vector3(point.x * 0.1 + center.x, point.y * 0.1 + center.y, point.z))
            }
        },
        loveAnimate(startArr, endArr) {
            let params = {
                time: 0
            }
            gsap.to(params, {
                time: 1,
                duration: 1,
                onUpdate: () => {
                    for (let i = 0; i < 100; i++) {
                        let x = startArr[i].x + (endArr[i].x - startArr[i].x) * params.time
                        let y = startArr[i].y + (endArr[i].y - startArr[i].y) * params.time
                        let z = startArr[i].z + (endArr[i].z - startArr[i].z) * params.time
                        let matrix = new THREE.Matrix4()
                        matrix.setPosition(x, y, z)
                        console.log(x, y, z)
                        this.instance.setMatrixAt(i, matrix)
                    }
                    this.instance.instanceMatrix.needsUpdate = true
                }
            })
        },
        loveRestartAnimate(startArr, endArr) {
            let params = {
                time: 0
            }
            gsap.to(params, {
                time: 1,
                duration: 1,
                onUpdate: () => {
                    for (let i = 0; i < 100; i++) {
                        let x = endArr[i].x + (startArr[i].x - endArr[i].x) * params.time
                        let y = endArr[i].y + (startArr[i].y - endArr[i].y) * params.time
                        let z = endArr[i].z + (startArr[i].z - endArr[i].z) * params.time
                        let matrix = new THREE.Matrix4()
                        matrix.setPosition(x, y, z)
                        this.instance.setMatrixAt(i, matrix)
                    }
                    this.instance.instanceMatrix.needsUpdate = true
                }
            })
        }
    }
}
</script>
<style>
html,
body {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
}

#home {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
}
</style>